package controllers

import (
	"crypto/md5"
	"encoding/hex"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	"github.com/astaxie/beego/orm"
	"github.com/dgrijalva/jwt-go"
	"github.com/satori/go.uuid"
	"io"
	"reflect"
	"smallgift/models"
	"strings"
	"time"
)

type Router struct {
	beego.Controller
	Params  BaseRequestParams
	MchUser models.UserInfo
	Opkey   string
}

type BaseRequestParams struct {
	Scheme     string
	Method     string
	RemoteAddr string
	RemotePort string
	Params     map[string]string
	Service    string
	UserAgent  string
	EnterTime  string
}

const (
	OPKEY_LOGIN  = "LOGIN"
	OPKEY_REFUND = "REFUND"
)

func (this *Router) FilterBeforeRouter(ctx *context.Context) map[string]interface{} {
	//解析本次请求参数
	this.Params = BaseRequestParams{}
	this.Params.EnterTime = time.Now().Format("2006-01-02 15:04:05")
	this.Params.Method = ctx.Input.Method()
	this.Params.RemoteAddr = this.Ctx.Request.Header.Get("GF_PRIV_FORWARD_IP")
	if len(this.Params.RemoteAddr) == 0 {
		this.Params.RemoteAddr = this.Ctx.Request.Header.Get("X-Forwarded-For")
	}
	if len(this.Params.RemoteAddr) == 0 {
		this.Params.RemoteAddr = this.Ctx.Request.RemoteAddr
		if strings.Contains(this.Params.RemoteAddr, ":") {
			ps := strings.Split(this.Params.RemoteAddr, ":")
			this.Params.RemoteAddr = ps[0]
			this.Params.RemotePort = ps[1]
		}
	}
	beego.Error(this.Params.RemoteAddr)
	this.Params.Scheme = ctx.Input.Scheme()
	__map_req := map[string]string{}
	for k, v_arr := range ctx.Request.Form {
		__map_req[k] = v_arr[0]
	}
	this.Params.Params = __map_req
	beego.Error(ctx.Request.URL.String())
	if x := ctx.Request.URL.String(); strings.Contains(x, "?") {
		this.Params.Service = x[:strings.Index(x, "?")]
	} else {
		this.Params.Service = x
	}
	this.Params.UserAgent = this.Ctx.Request.Header.Get("User-Agent")

	//4.校验 TOKEN
	if !strings.Contains(this.Params.Service, "/user/login") {
		token := this.GetStr("Authorization", "")
		if len(token) == 0 {
			beego.Error("token 为空")
			return this.GenErrResp("AUTH_TOKEN不合法")
		}
		if x, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
			return this.GetSecretKey(), nil
		}); err != nil {
			beego.Error(err.Error())
			if !x.Valid {
				beego.Error("验证不成功")
				return this.GenErrRespEx(ERR_USER_RELOGIN, "TOKEN已失效,请重新登录")
			} else {
				return this.GenErrResp("AUTH_TOKEN不合法")
			}
		} else if !x.Valid {
			beego.Error("验证不成功")
			return this.GenErrRespEx(ERR_USER_RELOGIN, "TOKEN已失效,请重新登录")
		} else if claims, ok := x.Claims.(jwt.MapClaims); !ok {
			beego.Error("JWT报文不合法")
			return this.GenErrResp("AUTH_TOKEN不合法")
		} else if usr_uuid := claims["sub"].(string); len(usr_uuid) == 0 {
			beego.Error("用户信息不完整")
			return this.GenErrResp("AUTH_TOKEN不合法")
		} else {
			//获取用户信息.
			if this.MchUser, err = models.ReadUserInfoByUUID(usr_uuid); err != nil && err != orm.ErrNoRows {
				return this.GenErrResp(err.Error())
			} else if err == orm.ErrNoRows {
				return this.GenErrResp("无此用户信息")
			} else if this.MchUser.CurrentToken != token {
				return this.GenErrRespEx(ERR_USER_RELOGIN, GetErrMsg(ERR_USER_RELOGIN))
			}
		}
	}

	return nil
}

//通用响应值
func (this *Router) genResp(data interface{}, p ...string) map[string]interface{} {
	m := struct_to_map(data)
	if len(p) == 0 {
		m["code"] = CODE_ERROR
		m["msg"] = GetErrMsg(CODE_ERROR)
	} else if len(p) == 1 {
		m["code"] = p[0]
		m["msg"] = GetErrMsg(p[0])
	} else if len(p) == 2 {
		m["code"] = p[0]
		m["msg"] = p[1]
	}
	return m
}

func (this *Router) GenUUID() string {
	return strings.Replace(uuid.Must(uuid.NewV4()).String(), "-", "", -1)
}

func (this *Router) GetSecretKey() []byte {
	return []byte("TOBEORNOTTOBETHATSAQUESTION")
}

//通用成功值
func (this *Router) GenSucResp(msg string) map[string]interface{} {
	return this.genResp(nil, CODE_SUCCESS, msg)
}

//通用错误值
func (this *Router) GenErrResp(msg string) map[string]interface{} {
	return this.genResp(nil, CODE_ERROR, msg)
}

//通用错误值
func (this *Router) GenErrRespEx(code, msg string) map[string]interface{} {
	return this.genResp(nil, code, msg)
}

func (this *Router) checkTokenValid(token string) bool {
	if x, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return this.GetSecretKey(), nil
	}); err != nil {
		beego.Error(err.Error())
		return false
	} else if !x.Valid {
		beego.Error("验证不成功")
		return false
	} else if _, ok := x.Claims.(jwt.MapClaims); !ok {
		beego.Error("JWT报文不合法")
		return false
	}
	return true
}

func (this *Router) GetStr(key, default_value string) string {
	x := this.GetString(key, default_value)
	x = strings.Trim(x, " ")
	return x
}

func (this *Router) MD5(in string) string {
	m := md5.New()
	io.WriteString(m, in)
	return strings.ToUpper(hex.EncodeToString(m.Sum(nil)))
}

func struct_to_map(x interface{}) map[string]interface{} {
	var data = make(map[string]interface{})
	if x == nil {
		return data
	}
	t := reflect.TypeOf(x)
	v := reflect.ValueOf(x)

	for i := 0; i < t.NumField(); i++ {
		key := t.Field(i).Name
		value := v.Field(i).Interface()
		tag := t.Field(i).Tag.Get("json")
		if tag != "" {
			if strings.Contains(tag, ",") {
				ps := strings.Split(tag, ",")
				key = ps[0]
			} else {
				key = tag
			}
		}
		data[key] = value
	}
	return data
}

func (this *Router) Prepare() {
	//PREPARE中停止的Controller 是不会执行到 Finish函数的.
	//需要处理响应信息日志.
	if this.Ctx.Request.Method == "HEAD" {
		return
	}
	//设置公共的同步.防止跨域
	this.Ctx.Output.Header("Access-Control-Expose-Headers", "Authorization")
	beego.Debug(this.Ctx.Request.Header.Get("Origin"))
	this.Ctx.Output.Header("Cache-Control", "no-store")
	if v := this.Ctx.Request.Header.Get("Origin"); len(v) > 0 {
		for _, x := range allowed_origin_list {
			if x == v {
				this.Ctx.Output.Header("Access-Control-Allow-Origin", v)
				break
			}
		}
	}

	this.Ctx.Output.Header("Access-Control-Allow-Method", "POST")
	this.Ctx.Output.Header("Access-Control-Allow-Method", "GET")
	this.Ctx.Output.Header("Access-Control-Allow-Origin", "*")

	//预处理业务逻辑
	if x := this.FilterBeforeRouter(this.Ctx); x != nil {
		this.Data["json"] = x
		this.ServeJSON()
		this.StopRun()
		return
	}
}

var allowed_origin_list = []string{
	"http://localhost:40059",
	"http://127.0.0.1:63342",
	"http://localhost:9529",
	"http://c.guangfen.net",
	"c.guangfen.net",
	"b.miaofutech.com",
	"http://b.miaofutech.com",
}

func (this *Router) Finish() {
	if this.Ctx.Request.Method == "HEAD" {
		return
	}
}

/*********************************/

type BaseReturnBean struct {
	ReturnData
	Data string `json:"data"`
}

type ReturnData struct {
	Code string `json:"code"`
	Msg  string `json:"msg"`
}

type OrdersBean struct {
	ReturnData
	TotalNum  int            `json:"total_num"`
	TotalPage int            `json:"total_page"`
	Page      int            `json:"page"`
	Rows      int            `json:"rows"`
	List      []NewOrderbean `json:"list"`
}

type TotalNum struct {
	TotalNum string `json:"total_num"`
}

type Order struct {
	ReturnData
	NewOrderbean
}

type H5Order struct {
	ReturnData
	Data []H5OrderBean `json:"data"`
}

type H5OrderBean struct {
	Name           string `json:"name"`
	ExpressCompany string `json:"express_company"`
	CourierNo      string `json:"courier_no"`
	SendStatus     string `json:"send_status"`
	Amount         string `json:"amount"`
	Province       string `json:"province"`
	City           string `json:"city"`
	Area           string `json:"area"`
	Address        string `json:"address"`
	TicketNo       string `json:"ticket_no"`
	ReceiveDate    string `json:"receive_date"`
}

type NewOrderbean struct {
	TicketNo       string `json:"ticket_no"`
	OrderNo        string `json:"order_no"`
	SendStatus     string `json:"send_status"`
	CourierNo      string `json:"courier_No"`
	ExpressCompany string `json:"express_company"`
	Name           string `json:"name"`
	Phone          string `json:"phone"`
	Updated        string `json:"updated"`
	Province       string `json:"province"`
	City           string `json:"city"`
	Area           string `json:"area"`
	Address        string `json:"address"`
	ActivityId     string `json:"activity_id"`
	ActivityName   string `json:"activity_name"`
	ReceiveDate    string `json:"receive_date"`
}

type ApiBaseController struct {
	beego.Controller
}

type BaseBean struct {
	Code    string `json:"code"`
	Message string `json:"msg"`
}

type Activitybean struct {
	Id             int    `json:"id"`
	ActivityName   string `json:"activity_name"`
	ActivityAmount string `json:"activity_amount"`
	Status         string `json:"status"`
	MiddleNum      string `json:"middle_num"`
	TopRule        string `json:"top_rule"`
	TailRule       string `json:"tail_rule"`
	Created        string `json:"created"`
}

type ActivitysBean struct {
	ReturnData
	TotalNum  int            `json:"total_num"`
	TotalPage int            `json:"total_page"`
	Page      int            `json:"page"`
	Rows      int            `json:"rows"`
	List      []Activitybean `json:"list"`
}

func (this *ApiBaseController) Prepare() {
	this.Ctx.Output.Header("Access-Control-Allow-Method", "POST")
	this.Ctx.Output.Header("Access-Control-Allow-Method", "GET")
	this.Ctx.Output.Header("Access-Control-Allow-Method", "DELETE")
	this.Ctx.Output.Header("Access-Control-Allow-Method", "PUT")
	this.Ctx.Output.Header("Access-Control-Allow-Method", "OPTIONS")
	this.Ctx.Output.Header("Access-Control-Allow-Origin", "*")
}
